package cn.ingenic.glasssync.contact;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import cn.ingenic.glasssync.contact.ContactsProvider;
import cn.ingenic.glasssync.db.ContactsDBHelper;
import cn.ingenic.glasssync.services.*;
import cn.ingenic.glasssync.R;
import com.ingenic.glass.voicerecognizer.api.VoiceRecognizer;
import com.ingenic.glass.voicerecognizer.api.VoiceRecognizerListener;

public class ContactsModule extends SyncModule {
        private static final String TAG = "ContactsModule";
        private static boolean DEBUG = false;

        private static final String MODULE_NAME = "contacts_module";

        private final String KEY_TYPE = "type";
        private final String KEY_SYNCCMD = "cmd";
        private final String KEY_SYNCNAME = "name";
        private final String KEY_SYNCPHONE = "phone";
        private final String KEY_SYNCTYPE = "type";
        private final String KEY_SYNCALL = "alljson";
        private final String KEY_CONTACTSLEN = "contacts_len";

        private static final String JKEY_ADD = "add";
        private static final String JKEY_DEL_NAME = "del_name";
        private static final String JKEY_DEL_DATA = "del_data";
        private static final String JKEY_NAME = "display_name";
        private static final String JKEY_DATA = "data";

        private final int TYPE_SYNC = 0;

        private final int CMD_ADD = 0;
        private final int CMD_DELNAME = 1;
        private final int CMD_DELPHONE = 2;
        private final int CMD_CLEARALL = 3;
        private final int CMD_SYNCALLLEN = 4;
        private final int CMD_SYNCALL = 5;
        private final int CMD_RESYNCALL = 6; // Retry sync all contacts.

        private static ContactsModule mInstance = null;
	private VoiceRecognizer mVoiceRecognizer = null;
	private SQLiteDatabase mContactDB = null;
	private PowerManager.WakeLock mWakeLock = null;
        private Context mContext = null;
        private ContentResolver mResolver = null;
        private BlockingQueue<SyncData> mSyncDataQueue = null;
        private int mBondState = 0; // 1 is binded; 0 is unbind.
        private boolean mIsBindAndroidDevice;
        private ContactsModule(String name, Context context) {
                super(name, context);
                if (DEBUG) Log.d(TAG, "ContactsModule init...");
                mContext = context;
                mResolver = context.getContentResolver();
		String bond_addr = Settings.System.getString(mResolver,"glasssync_bond_addr");
		if(null == bond_addr || bond_addr.equals("")){
		    mBondState = 0;
		    mIsBindAndroidDevice = false;
		}else if(bond_addr.indexOf("-")<0){
		    mIsBindAndroidDevice = true;
		    mBondState = 1;
		}else{
		    mIsBindAndroidDevice = false;
		    mBondState = 1;
		}
                Uri bindStateUri = Settings.System.getUriFor("glasssync_bond_addr");
                mContext.getContentResolver().registerContentObserver(bindStateUri,false,mContentObserver);
                mSyncDataQueue = new LinkedBlockingQueue<SyncData>();
                new Thread(new HandleQueue()).start();
		mVoiceRecognizer = new VoiceRecognizer(VoiceRecognizer.REC_TYPE_COMMAND, 
						       new VoiceRecognizerListener());
		SQLiteOpenHelper dBHelper = ContactsDBHelper.getInstance(context, null);
		mContactDB = dBHelper.getWritableDatabase();
		PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
		mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mContext.getClass().getName());
        }

        public static ContactsModule getInstance (Context context) {
                if (null == mInstance) {
                        mInstance = new ContactsModule(MODULE_NAME, context);
                }
                return mInstance;
        }

        @Override
        protected void onCreate() {
        }

        @Override
        protected void onRetrive(SyncData data) {
                super.onRetrive(data);
                int type = data.getInt(KEY_TYPE);
                if (DEBUG) Log.i(TAG, "onRetrive() in, type=" + type);
                mSyncDataQueue.offer(data);
        }

        private void sendRetrySyncAll () {
                SyncData data = new SyncData();
                data.putInt(KEY_SYNCCMD, CMD_RESYNCALL);
                try {
                        send(data);
                } catch (SyncException e) {
                        e.printStackTrace();
                }
        }

        class HandleQueue implements Runnable {

                @Override
                public void run() {
                        boolean isTakeData = false;
                        SyncData data = null;
                        while (!Thread.interrupted()) {
                                if (!isTakeData) {
                                        try {
                                                data = mSyncDataQueue.take();
                                        } catch (Exception e) {
                                                e.printStackTrace();
                                                continue ;
                                        }
                                }
                                isTakeData = false;
                                int type = data.getInt(KEY_TYPE);
                                switch (type) {
                                case TYPE_SYNC: {
                                        int cmd = data.getInt(KEY_SYNCCMD);
                                        if (CMD_ADD == cmd) {
                                                if (DEBUG) Log.d(TAG, "CMD_ADD");
                                                addContacts(data);
                                        } else if (CMD_DELNAME == cmd) { // Delete contacts.
                                                delContacts(data);
                                        } else if (CMD_DELPHONE == cmd) { // Delete contacts phone.
                                                delPhone(data);
                                        } else if (CMD_CLEARALL == cmd) { // Clear all contacts.
                                                if (DEBUG) Log.d(TAG, "CMD_CLEARALL");
                                                mResolver.delete(ContactsProvider.URI_CLEARALL, null, null);
                                        } else if (CMD_SYNCALLLEN == cmd) {
                                                long jsonStrLen = data.getLong(KEY_CONTACTSLEN);
                                                if (DEBUG) Log.d(TAG, "CMD_SYNCALLLEN, jsonStrLen = " + jsonStrLen);
                                                SyncData contactsData = null;
                                                try {
                                                        contactsData = mSyncDataQueue.take();
                                                } catch (InterruptedException e) {
                                                        e.printStackTrace();
                                                }
                                                if ((null == contactsData)
                                                                || (TYPE_SYNC != contactsData.getInt(KEY_TYPE))
                                                                || (CMD_SYNCALL != contactsData.getInt(KEY_SYNCCMD))) {
                                                        Log.e(TAG, "Invalid CMD_SYNCALLLEN, Must immediately receive CMD_SYNCALL.");
                                                        isTakeData = true;
                                                        sendRetrySyncAll();
                                                        continue;
                                                }
                                                if (DEBUG) Log.d(TAG, "CMD_SYNCALL");
                                                try {
                                                        syncAllContacts(jsonStrLen, contactsData);
                                                } catch (Exception e) {
                                                        e.printStackTrace();
                                                }
                                        } else if (CMD_SYNCALL == cmd) {
                                                Log.e(TAG, "Invalid CMD_SYNCALL: Must first receive CMD_SYNCALLLEN.");
                                                sendRetrySyncAll();
                                        } else {
                                                Log.w(TAG, "Unknow cmd: " + cmd);
                                        }
                                        break;
                                }
                                default:
                                        Log.w(TAG, "Unknow type: " + type);
                                        break;
                                }
                        }
                }

                private void syncAllContacts(long jsonStrLen, SyncData data)
                                throws JSONException {
                        String contactsJsonStr = data.getString(KEY_SYNCALL);
                        if (jsonStrLen != contactsJsonStr.length()) {
                                Log.e(TAG, "Invalid contactsJsonStr len. jsonStrLen = " + jsonStrLen
                                                + ", contactsJsonStr = " + contactsJsonStr.length());
                                sendRetrySyncAll();
                                return ;
                        }
                        if (DEBUG) Log.d(TAG, contactsJsonStr);
                        JSONObject allContactsJsonObj = new JSONObject(contactsJsonStr);
                        JSONArray addContactsJsonAry = allContactsJsonObj.getJSONArray(JKEY_ADD);
                        JSONArray delContactsJsonAry = allContactsJsonObj.getJSONArray(JKEY_DEL_NAME);
                        JSONArray delDataJsonAry = allContactsJsonObj.getJSONArray(JKEY_DEL_DATA);

			if (!mWakeLock.isHeld())
				mWakeLock.acquire();
			mContactDB.beginTransaction(); //开始事务
			try {
				// Add contacts.
				for (int i = 0, j = addContactsJsonAry.length(); (i < j) && (0 != mBondState); i++) {
					JSONObject contactsJsonObj = addContactsJsonAry.getJSONObject(i);
					SyncData contactsData = new SyncData();
					contactsData.putString(KEY_SYNCNAME, contactsJsonObj.getString(JKEY_NAME));
					contactsData.putString(KEY_SYNCPHONE, contactsJsonObj.getString(JKEY_DATA));
					addContacts(contactsData);
				}
				// Delete contacts.
				for (int i = 0, j = delContactsJsonAry.length(); (i < j) && (0 != mBondState); i++) {
					JSONObject contactsJsonObj = delContactsJsonAry.getJSONObject(i);
					SyncData contactsData = new SyncData();
					contactsData.putString(KEY_SYNCNAME, contactsJsonObj.getString(JKEY_NAME));
					delContacts(contactsData);
				}
				// Delete contacts data.
				for (int i = 0, j = delDataJsonAry.length(); (i < j) && (0 != mBondState); i++) {
					JSONObject dataJsonObj = delDataJsonAry.getJSONObject(i);
					SyncData contactsData = new SyncData();
					contactsData.putString(KEY_SYNCNAME, dataJsonObj.getString(JKEY_NAME));
					contactsData.putString(KEY_SYNCPHONE, dataJsonObj.getString(JKEY_DATA));
					delPhone(contactsData);
				}
				if (mBondState != 0)
					mContactDB.setTransactionSuccessful(); //设置事务处理成功，不设置会自动回滚不提交
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				mContactDB.endTransaction();//结束事务
				if (mBondState != 0) {
					mResolver.notifyChange(ContactsProvider.URI_CONTACTS, null);
					mResolver.notifyChange(ContactsProvider.URI_DATA, null);
				}
				mWakeLock.release();
			}
			// playTTS
			int contactCount = 0;
			String[] contactsCols = new String [] { "_id", "display_name" };
			Cursor cursor = mResolver.query(ContactsProvider.URI_CONTACTS, 
							contactsCols, null, null, null);
			if (null != cursor) {
				contactCount = cursor.getCount();
				cursor.close();
			}
			if (contactCount > 0) {
				String tts = String.format(mContext.getString(R.string.tts_contact_sync_ok), 
							   ""+contactCount);
				mVoiceRecognizer.playTTS(tts);
			}
                }

        }

        private long queryContactId (String displayName) {
                long contact_id = -1;
                Cursor cursor = mResolver.query(ContactsProvider.URI_CONTACTS
                                , new String [] { "_id" }
                                , " display_name = ? "
                                , new String [] { displayName }
                                , null);
                if (null != cursor) {
                        if (cursor.moveToFirst()) {
                                contact_id = cursor.getLong(cursor.getColumnIndex("_id"));
                        }
                        cursor.close();
                }
                return contact_id;
        }

        private void addContacts (SyncData data) {
                ContentValues values = new ContentValues();
                long contact_id = -1;
                String name = data.getString(KEY_SYNCNAME);
                String phone = data.getString(KEY_SYNCPHONE);
                phone = phone.replaceAll("-", "");
                if (DEBUG) Log.d(TAG, "name = " + name + ", phone = " + phone);

                contact_id = queryContactId(name);
                if (DEBUG) Log.d(TAG, "contact_id = " + contact_id);
                if (contact_id < 0) { // contents is not exists.
                        // Insert to CONTACTS table.
                        values.put(ContactsProvider.KEY_CONTACTS_DISPLAYNAME, name);
                        Uri contactUri = mResolver.insert(ContactsProvider.URI_CONTACTS, values);
                        if (null != contactUri) {
                                contact_id = ContentUris.parseId(contactUri);
                        }
                        values.clear();
                }

                // Insert to DATA table.
                values.put(ContactsProvider.KEY_DATA_CONTACTID, contact_id);
                values.put(ContactsProvider.KEY_DATA_MIMETYPE, ContactsDBHelper.MIMETYPE_ITEM);
                values.put(ContactsProvider.KEY_DATA_TYPE, ContactsDBHelper.DATA_TYPE_PHONE);
                values.put(ContactsProvider.KEY_DATA_DATA, phone);
                mResolver.insert(ContactsProvider.URI_DATA, values);
        }

        private void delContacts(SyncData data) {
                long contact_id = queryContactId(data.getString(KEY_SYNCNAME));
                if (DEBUG) Log.d(TAG, "CMD_DELNAME, contact_id = " + contact_id);
                mResolver.delete(ContactsProvider.URI_CONTACTS
                                , "_id = ? "
                                , new String [] { contact_id + "" });
                mResolver.delete(ContactsProvider.URI_DATA
                                , "contact_id = ? "
                                , new String [] { contact_id + "" });
        }

        private void delPhone(SyncData data) {
                long contact_id = queryContactId(data.getString(KEY_SYNCNAME));
                String phone = data.getString(KEY_SYNCPHONE);
                phone = phone.replaceAll("-", "").replaceAll(" ", "");
                if (DEBUG) Log.d(TAG, "CMD_DELPHONE, contact_id=" + contact_id + ", phone=" + phone);
                mResolver.delete(ContactsProvider.URI_DATA
                                , "contact_id = ? AND type = ? AND data = ? "
                                , new String [] { contact_id + ""
                                                , ContactsDBHelper.DATA_TYPE_PHONE
                                                , phone });
        }

        private ContentObserver mContentObserver = new ContentObserver(new Handler()){
                @Override
                public void onChange(boolean selfChange) {
                    super.onChange(selfChange);
		    String bond_addr = Settings.System.getString(mContext.getContentResolver(),"glasssync_bond_addr");
		    if (DEBUG)Log.d(TAG,"bond_addr="+bond_addr+",mIsBindAndroidDevice="+mIsBindAndroidDevice);
		    if(null == bond_addr || bond_addr.equals("")){
			mBondState = 0;
			mSyncDataQueue.clear();
			if(mIsBindAndroidDevice){
			    mResolver.delete(ContactsProvider.URI_CLEARALL, null, null);
			}
		    }else if(bond_addr.indexOf("-")<0){
			mIsBindAndroidDevice = true;
			mBondState = 1;
		    }else{
			mIsBindAndroidDevice = false;
			mBondState = 1;
		    }
                }
        };

}
